#pragma once

#include <iostream>
#include <math.h>

struct Telegram 
{	
	// Entity that sends message
	int		m_iSender;

	// Entity that will receive the message
	int		m_iReceiver;

	// The message
	int		m_iMessage;

	// Time Delay to send message
	double	m_dTimeDelay;

	// Additional information
	void*	m_extraInfo;

	// Default Constructor
	Telegram():m_dTimeDelay(-1),
			  m_iSender(-1),
		      m_iReceiver(-1),
		m_iMessage(-1)
	{}

	// Constructor
	Telegram(double dTimeDelay,
		int    iSender,
		int		iReceiver,
		int    iMessage,
		void*  extraInfo = NULL):	m_dTimeDelay(dTimeDelay),
									m_iSender(iSender),
									m_iReceiver(iReceiver),
									m_iMessage(iMessage),
									m_extraInfo(extraInfo)
	{}
};

// These messages will be stored in a priority queue. Therefore the >
// operator needs to be overloaded so that the PQ can sort the telegrams
// by time priority. Note how the times must be smaller than
// SmallestDelay apart before two Telegrams are considered unique.
const double dSmallestDelay = 0.25;

inline bool operator==(const Telegram& messageOne, const Telegram& messageTwo)
{
	return (fabs(messageOne.m_dTimeDelay-messageTwo.m_dTimeDelay) < dSmallestDelay) &&
		(messageOne.m_iSender == messageTwo.m_iSender)        &&
		(messageOne.m_iReceiver == messageTwo.m_iReceiver)    &&
		(messageOne.m_iMessage == messageTwo.m_iMessage);
}

inline bool operator<(const Telegram& messageOne, const Telegram& messageTwo)
{
	if (messageOne == messageTwo)
	{
		return false;
	}

	else
	{
		return  (messageOne.m_dTimeDelay < messageTwo.m_dTimeDelay);
	}
}

inline std::ostream& operator<<(std::ostream& os, const Telegram& newMessage)
{
	os << "time: " << newMessage.m_dTimeDelay << "  Sender: " << newMessage.m_iSender
		<< "   Receiver: " << newMessage.m_iReceiver << "   Msg: " << newMessage.m_iMessage;

	return os;
}

//handy helper function for dereferencing the ExtraInfo field of the Telegram 
//to the required type.
template <class T>
inline T DereferenceToType(void* p)
{
	return *(T*)(p);
}